/*
  Copyright 2011 Faculte Polytechnique de Mons (TCTS lab)

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "sekai/OLABuffer.h"

// Source code take from
//<https://github.com/numediart/mage-legacy/blob/master/magelib-2.00/libs/olabox-0.01>,
// this version contains BugFixes

OLABuffer::OLABuffer(int bufferLen) {
  length = bufferLen;  // init the OLA buffer to 0
  rawData = new float[length];
  memset(rawData, 0, length * sizeof(float));
  pos = 0;  // 1st position is buffer head
  inputPosition = 0;
  atLoc = 0;
}

OLABuffer::~OLABuffer() { delete[] rawData; }

void OLABuffer::ola(float *frame, int frameLen, float period) {
  int m, k;

  float a = atLoc - ((int)atLoc);
  float b = 1 - a;
  float nextSample = 0;

  for (k = 0; k < frameLen; k++) {
    m = (pos + ((int)atLoc) + k) %
        length;  // we cycle through OLA buffer indices
    if (k < frameLen - 1) nextSample = frame[k + 1];
    rawData[m] += frame[k] * a + nextSample * b;  // and add the current frame
                                                  // at location and apply
                                                  // fractional delay
  }

  atLoc += period;  // buffer location
  inputPosition += period;
}

void OLABuffer::ola(double *frame, int frameLen, float period) {
  int m, k;

  float a = atLoc - ((int)atLoc);
  float b = 1 - a;
  float nextSample = 0;

  for (k = 0; k < frameLen; k++) {
    m = (pos + ((int)atLoc) + k) %
        length;  // we cycle through OLA buffer indices
    if (k < frameLen - 1) nextSample = frame[k + 1];
    rawData[m] += frame[k] * a + nextSample * b;  // and add the current frame
                                                  // at location and apply
                                                  // fractional delay
  }

  atLoc += period;  // buffer location
  inputPosition += period;
}

bool OLABuffer::isFilled(int size) { return atLoc > size; }

void OLABuffer::pop(float *buffer, int bufferLen) {
  if (pos + bufferLen < length) {
    // transfer samples to out in one block
    memcpy(buffer, &rawData[pos], bufferLen * sizeof(float));
    memset(&rawData[pos], 0, bufferLen * sizeof(float));

  } else {
    // remaining bit
    int remain = length - pos;

    // transfer/clean from pos to end
    memcpy(buffer, &rawData[pos], remain * sizeof(float));
    memset(&rawData[pos], 0, remain * sizeof(float));

    // transfer/clean from beginning to remaining bit
    memcpy(&buffer[remain], rawData, (bufferLen - remain) * sizeof(float));
    memset(rawData, 0, (bufferLen - remain) * sizeof(float));
  }

  // finally move pos to next
  pos = (pos + bufferLen) % length;

  atLoc -= bufferLen;
}

void OLABuffer::reset() {
  pos = 0;  // 1st position is buffer head
  atLoc = 0;
  inputPosition = 0;
  memset(rawData, 0, length * sizeof(float));
}
